﻿// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Mono.Cecil;

namespace ICSharpCode.Decompiler.ILAst
{

	public class ILTryCatchBlockInfo
	{
		public ILTryCatchBlock blk;
		public List<ILNode>    nodelistRef;
	}

	public class YieldVMStat
	{
		public int scanningStat;
		public int fieldStat;
		public int localStat;
		public int localDisposeFlag;
		public int localReturnValue;
		public ILVariable statILVariable = null;
		public ILVariable diposeFlagILVariable = null;
		public ILExpression firstStatSwitchExpr;
		private Dictionary<int, ILNode>  newBody = new  Dictionary<int, ILNode>();
		public List<ILNode>   usefulList = new  List<ILNode>();
		public Dictionary<ILLabel,int> labelMaping = new  Dictionary<ILLabel,int> ();
		public List<ILLabel>	 		fistStateSwitchLabelList = new List<ILLabel> ();

		public Dictionary<int,int>   fieldStatChangedPos = new Dictionary<int,int>();
		public Dictionary<int,int>   localStatChangedPos = new Dictionary<int,int>();
		public Dictionary<int,ILTryCatchBlockInfo> tryBlockMapping = new Dictionary<int,ILTryCatchBlockInfo> ();

		public bool markingLocalChangePos = false;
		private int maxCodePos = -1;
		public void resetStatVariable()
		{
			fieldStat = 0;
			fieldStat = 0;
			localDisposeFlag = 0;
			localReturnValue = 0;
			localStatChangedPos.Clear ();
            markingLocalChangePos = false;
		}

		public bool addNewNode(ILNode node, int pos)
		{
			if (newBody.ContainsKey (pos)) {
				return false;
			}


			if (pos > maxCodePos) {
				maxCodePos = pos;
			}
			this.newBody.Add (pos,node);
			return true;
		}

		public bool addCreateNewNode(ILNode node, int pos)
		{
			
			if (newBody.ContainsKey (pos)) {
				return addNewNode (node, maxCodePos+1);	
			} else {
				return addNewNode (node, pos);
			}
		}

        public void clearCreatedNodes()
        {
            maxCodePos = -1;
            this.newBody.Clear();
        }

		public List<ILNode> outputNewBody()
		{
			List<ILNode> b = new List<ILNode> ();
			ILNode lastNode = null; 
			HashSet<ILTryCatchBlock> addedTryCatchBlock = new HashSet<ILTryCatchBlock> ();

			for (int i = 0; i <= maxCodePos; i++) {
				if (this.newBody.ContainsKey (i)) {

					List<ILExpression> args = new List<ILExpression>();


					if (lastNode!=null ) {
						if (lastNode is ILLabel && this.newBody [i] is ILLabel) {
							b.Add (new ILExpression (ILCode.Nop, null, args));
						} else if (lastNode is ILExpression && this.newBody [i] is ILLabel
							&& (lastNode as ILExpression).Code== ILCode.Br 
							&&  ((lastNode as ILExpression).Operand as ILLabel).Name ==  (this.newBody [i] as ILLabel).Name
							 
						) {
							b.Remove (lastNode);
						}
							
							
					}
					lastNode = this.newBody [i];

					if (this.tryBlockMapping.ContainsKey (i)) {
						
						if (!addedTryCatchBlock.Contains(this.tryBlockMapping [i].blk)) 
						{
							addedTryCatchBlock.Add (this.tryBlockMapping [i].blk);
							b.Add (this.tryBlockMapping [i].blk);
						}
						this.tryBlockMapping [i].nodelistRef.Add (lastNode);
					} else {
						b.Add (lastNode);
					}

				}
			}
			if (!b.LastOrDefault ().IsUnconditionalControlFlow()) {
				b.Add (new ILExpression (ILCode.YieldBreak, null));
			}
			return b;
		}
	}

	class YieldReturnDecompiler
	{
		// For a description on the code generated by the C# compiler for yield return:
		// http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx
		
		// The idea here is:
		// - Figure out whether the current method is instanciating an enumerator
		// - Figure out which of the fields is the state field
		// - Construct an exception table based on states. This allows us to determine, for each state, what the parent try block is.
		
		// See http://community.sharpdevelop.net/blogs/danielgrunwald/archive/2011/03/06/ilspy-yield-return.aspx
		// for a description of this step.
		
		DecompilerContext context;
		TypeDefinition enumeratorType;
		MethodDefinition enumeratorCtor;
		MethodDefinition disposeMethod;
		FieldDefinition stateField;
		FieldDefinition currentField;
		MethodDefinition moveNextMethod;
		int 		initPCValue=0;
		Dictionary<FieldDefinition, ILVariable> fieldToParameterMap = new Dictionary<FieldDefinition, ILVariable>();
		List<ILNode> createdBody;



		#region Run() method
		public static void Run(DecompilerContext context, ILBlock method)
		{
			if (!context.Settings.YieldReturn)
				return; // abort if enumerator decompilation is disabled
			var yrd = new YieldReturnDecompiler();
			yrd.context = context;
			if (!yrd.MatchEnumeratorCreationPattern(method))
				return;
			yrd.enumeratorType = yrd.enumeratorCtor.DeclaringType;

			#if DEBUG
			if (Debugger.IsAttached) {
				yrd.Run();
			} else {
				#endif
				try {
					yrd.Run();
				} catch (SymbolicAnalysisFailedException) {
					return;
				}
				#if DEBUG
			}
			#endif
			method.Body.Clear();
			method.EntryGoto = null;
			method.Body.AddRange(yrd.createdBody);
			
			// Repeat the inlining/copy propagation optimization because the conversion of field access
			// to local variables can open up additional inlining possibilities.
			ILInlining inlining = new ILInlining(method);
			inlining.InlineAllVariables();
			inlining.CopyPropagation();
		}
		
		void Run()
		{
			//AnalyzeCtor();
			//AnalyzeCurrentProperty();
			ResolveIEnumerableIEnumeratorFieldMapping();
		//	ConstructExceptionTable();
			bool ret = AnalyzeMoveNext2();
			if (ret) {
				TranslateFieldsToLocalAccess ();
			} else {
				createdBody = new List<ILNode> ();
			}
		}
		#endregion
		
		#region Match the enumerator creation pattern
		bool MatchEnumeratorCreationPattern(ILBlock method)
		{
			if (method.Body.Count == 0)
				return false;
			ILExpression newObj;
			if (method.Body.Count == 1) {
				// ret(newobj(...))
				if (method.Body[0].Match(ILCode.Ret, out newObj))
					return MatchEnumeratorCreationNewObj(newObj, out enumeratorCtor);
				else
					return false;
			}
				

			// stloc(var_1, newobj(..)
			ILVariable var1=null;
			if (!method.Body[0].Match(ILCode.Stloc, out var1, out newObj))
				return false;
			if (!MatchEnumeratorCreationNewObj(newObj, out enumeratorCtor))
				return false;

			ILVariable var2=null;
			for (int i = 1; i < method.Body.Count; i++) {
				ILExpression expr = method.Body [i] as ILExpression;
				if (expr==null) {
					return false;
				}

				switch (expr.Code) {
				case ILCode.Stloc:
					if (expr.Arguments [0].Code == ILCode.Ldloc && expr.Arguments [0].Operand == var1) {
						var2 = expr.Operand as ILVariable;
					}
					break;
				case ILCode.Stfld:
					if (expr.Arguments [0].Code == ILCode.Ldloc && (expr.Arguments [0].Operand == var1 || expr.Arguments [0].Operand == var2 )
					) {
						FieldReference f = expr.Operand as FieldReference;
							if (f != null && (f.Name == "$PC" )) {
							if (expr.Arguments [1].Code != ILCode.Ldc_I4) {
								return false;
							}
							//do not assinged the initPCValue, it should be alwas zero should be fine, because 
//							IL_0000: ldarg.0
//							IL_0001: ldflda int32 GameUpdateManager/'<UpdateRoutine>c__Iterator7'::$PC
//							IL_0006: ldc.i4.0
//							IL_0007: ldc.i4.s -2
//							IL_0009: call int32 [mscorlib]System.Threading.Interlocked::CompareExchange(int32&, int32, int32)
//							IL_000e: ldc.i4.s -2
//							IL_0010: bne.un.s IL_0014

							//will set the pc vavlue to zero when it's some negative value
							//this.initPCValue = (int)expr.Arguments [1].Operand;

						} else {
							FieldReference storedField;
							ILExpression ldloc, loadParameter;
							if (!method.Body[i].Match(ILCode.Stfld, out storedField, out ldloc, out loadParameter))
								break;
							ILVariable loadedVar, loadedArg;

							if (!ldloc.Match (ILCode.Ldloc, out loadedVar) 
								|| (
									!loadParameter.Match (ILCode.Ldloc, out loadedArg)
									&& 
									(loadParameter.Code==ILCode.Ldobj 
									&& !loadParameter.Arguments[0].Match (ILCode.Ldloc, out loadedArg)
									)
								)
							)
								break;
							storedField = GetFieldDefinition(storedField);
							if (loadedVar != var1 || storedField == null || !loadedArg.IsParameter)
								break;
							fieldToParameterMap[(FieldDefinition)storedField] = loadedArg;
							
						}
					} else {
						return false;
					}
						

					break;
				case ILCode.Ldloc:
					if (expr.Operand != var1) {
						return false;
					}
					break;

				case ILCode.Ret:
					if (expr.Arguments.Count == 0)
						return false;
					if (
						!(expr.Arguments[0] is ILExpression)
						|| expr.Arguments[0].Code!= ILCode.Ldloc
						|| (expr.Arguments[0].Operand != var1 &&  expr.Arguments[0].Operand != var2) 

					) {
						return false;
					}
					 
					break;
				default:
					return false;
				}

			}
		

			return true;

		
		}
		
		static FieldDefinition GetFieldDefinition(FieldReference field)
		{
			return CecilExtensions.ResolveWithinSameModule(field);
		}
		
		static MethodDefinition GetMethodDefinition(MethodReference method)
		{
			return CecilExtensions.ResolveWithinSameModule(method);
		}
		
		bool MatchEnumeratorCreationNewObj(ILExpression expr, out MethodDefinition ctor)
		{
			// newobj(CurrentType/...::.ctor, ldc.i4(-2))
			ctor = null;
			if (expr.Code != ILCode.Newobj || expr.Arguments.Count != 0)
				return false;

			ctor = GetMethodDefinition(expr.Operand as MethodReference);
			if (ctor == null || ctor.DeclaringType.DeclaringType != context.CurrentType)
				return false;
			
			if (!IsCompilerGeneratorEnumerator (ctor.DeclaringType))
				return false;

			stateField =  ctor.DeclaringType.Fields.FirstOrDefault (v => v.Name == "$PC");
			if (stateField == null)
				return false;
			
			currentField =  ctor.DeclaringType.Fields.FirstOrDefault (v => v.Name == "$current");
			if (currentField == null)
				return false;

			disposeMethod = ctor.DeclaringType.Methods.FirstOrDefault(m => m.Name == "Dispose");
			if (disposeMethod == null)
				return false;

			moveNextMethod = ctor.DeclaringType.Methods.FirstOrDefault(m => m.Name == "MoveNext");
			if (moveNextMethod == null)
				return false;
			
			return true;
		}
		
		public static bool IsCompilerGeneratorEnumerator(TypeDefinition type)
		{
			if (!(type.DeclaringType != null && type.IsCompilerGenerated()))
				return false;
			foreach (TypeReference i in type.Interfaces) {
				if (i.Namespace == "System.Collections" && i.Name == "IEnumerator")
					return true;
			}
			return false;
		}
		#endregion
		
		#region Figure out what the 'state' field is (analysis of .ctor())
		/// <summary>
		/// Looks at the enumerator's ctor and figures out which of the fields holds the state.
		/// </summary>
		void AnalyzeCtor()
		{
//			stateField =  enumeratorCtor.DeclaringType.Fields.FirstOrDefault (v => v.Name == "$PC");

//			ILBlock method = CreateILAst(enumeratorCtor);
//			
//			foreach (ILNode node in method.Body) {
//				FieldReference field;
//				ILExpression instExpr;
//				ILExpression stExpr;
//				ILVariable arg;
//				if (node.Match(ILCode.Stfld, out field, out instExpr, out stExpr) &&
//				    instExpr.MatchThis() &&
//				    stExpr.Match(ILCode.Ldloc, out arg) &&
//				    arg.IsParameter && arg.OriginalParameter.Index == 0)
//				{
//					stateField = GetFieldDefinition(field);
//				}
//			}
//			if (stateField == null)
//				throw new SymbolicAnalysisFailedException();


		}
		
		/// <summary>
		/// Creates ILAst for the specified method, optimized up to before the 'YieldReturn' step.
		/// </summary>
		ILBlock CreateILAst(MethodDefinition method)
		{
			if (method == null || !method.HasBody)
				throw new SymbolicAnalysisFailedException();
			
			ILBlock ilMethod = new ILBlock();
			ILAstBuilder astBuilder = new ILAstBuilder();
			ilMethod.Body = astBuilder.Build(method, true, context);
			ILAstOptimizer optimizer = new ILAstOptimizer();
			optimizer.Optimize(context, ilMethod, ILAstOptimizationStep.YieldReturn);
			return ilMethod;
		}
		#endregion
		
		#region Figure out what the 'current' field is (analysis of get_Current())
		/// <summary>
		/// Looks at the enumerator's get_Current method and figures out which of the fields holds the current value.
		/// </summary>
		void AnalyzeCurrentProperty()
		{
//			MethodDefinition getCurrentMethod = enumeratorType.Methods.FirstOrDefault(
//				m => m.Name.StartsWith("System.Collections.Generic.IEnumerator", StringComparison.Ordinal)
//				&& m.Name.EndsWith(".get_Current", StringComparison.Ordinal));
//			ILBlock method = CreateILAst(getCurrentMethod);
//			if (method.Body.Count == 1) {
//				// release builds directly return the current field
//				ILExpression retExpr;
//				FieldReference field;
//				ILExpression ldFromObj;
//				if (method.Body[0].Match(ILCode.Ret, out retExpr) &&
//				    retExpr.Match(ILCode.Ldfld, out field, out ldFromObj) &&
//				    ldFromObj.MatchThis())
//				{
//					currentField = GetFieldDefinition(field);
//				}
//			} else if (method.Body.Count == 2) {
//				ILVariable v, v2;
//				ILExpression stExpr;
//				FieldReference field;
//				ILExpression ldFromObj;
//				ILExpression retExpr;
//				if (method.Body[0].Match(ILCode.Stloc, out v, out stExpr) &&
//				    stExpr.Match(ILCode.Ldfld, out field, out ldFromObj) &&
//				    ldFromObj.MatchThis() &&
//				    method.Body[1].Match(ILCode.Ret, out retExpr) &&
//				    retExpr.Match(ILCode.Ldloc, out v2) &&
//				    v == v2)
//				{
//					currentField = GetFieldDefinition(field);
//				}
//			}
//			if (currentField == null)
//				throw new SymbolicAnalysisFailedException();
		}
		#endregion
		
		#region Figure out the mapping of IEnumerable fields to IEnumerator fields  (analysis of GetEnumerator())
		void ResolveIEnumerableIEnumeratorFieldMapping()
		{
			MethodDefinition getEnumeratorMethod = enumeratorType.Methods.FirstOrDefault(
				m => m.Name.StartsWith("System.Collections.Generic.IEnumerable", StringComparison.Ordinal)
				&& m.Name.EndsWith(".GetEnumerator", StringComparison.Ordinal));
			if (getEnumeratorMethod == null)
				return; // no mappings (maybe it's just an IEnumerator implementation?)
			
			ILBlock method = CreateILAst(getEnumeratorMethod);
			foreach (ILNode node in method.Body) {
				FieldReference stField;
				ILExpression stToObj;
				ILExpression stExpr;
				FieldReference ldField;
				ILExpression ldFromObj;
				if (node.Match(ILCode.Stfld, out stField, out stToObj, out stExpr) &&
				    stExpr.Match(ILCode.Ldfld, out ldField, out ldFromObj) &&
				    ldFromObj.MatchThis())
				{
					FieldDefinition storedField = GetFieldDefinition(stField);
					FieldDefinition loadedField = GetFieldDefinition(ldField);
					if (storedField != null && loadedField != null) {
						ILVariable mappedParameter;
						if (fieldToParameterMap.TryGetValue(loadedField, out mappedParameter))
							fieldToParameterMap[storedField] = mappedParameter;
					}
				}
			}
		}
		#endregion
		
		#region Construction of the exception table (analysis of Dispose())
		// We construct the exception table by analyzing the enumerator's Dispose() method.
		
		// Assumption: there are no loops/backward jumps
		// We 'run' the code, with "state" being a symbolic variable
		// so it can form expressions like "state + x" (when there's a sub instruction)
		// For each instruction, we maintain a list of value ranges for state for which the instruction is reachable.
		// This is (int.MinValue, int.MaxValue) for the first instruction.
		// These ranges are propagated depending on the conditional jumps performed by the code.
		
		Dictionary<MethodDefinition, StateRange> finallyMethodToStateRange;
		
		void ConstructExceptionTable()
		{
			disposeMethod = enumeratorType.Methods.FirstOrDefault(m => m.Name == "Dispose");
			ILBlock ilMethod = CreateILAst(disposeMethod);
			
			var rangeAnalysis = new StateRangeAnalysis(ilMethod.Body[0], StateRangeAnalysisMode.IteratorDispose, stateField);
			rangeAnalysis.AssignStateRanges(ilMethod.Body, ilMethod.Body.Count);
			finallyMethodToStateRange = rangeAnalysis.finallyMethodToStateRange;
			
			// Now look at the finally blocks:
			foreach (var tryFinally in ilMethod.GetSelfAndChildrenRecursive<ILTryCatchBlock>()) {
				var range = rangeAnalysis.ranges[tryFinally.TryBlock.Body[0]];
				var finallyBody = tryFinally.FinallyBlock.Body;
				if (finallyBody.Count != 2)
					throw new SymbolicAnalysisFailedException();
				ILExpression call = finallyBody[0] as ILExpression;
				if (call == null || call.Code != ILCode.Call || call.Arguments.Count != 1)
					throw new SymbolicAnalysisFailedException();
				if (!call.Arguments[0].MatchThis())
					throw new SymbolicAnalysisFailedException();
				if (!finallyBody[1].Match(ILCode.Endfinally))
					throw new SymbolicAnalysisFailedException();
				
				MethodDefinition mdef = GetMethodDefinition(call.Operand as MethodReference);
				if (mdef == null || finallyMethodToStateRange.ContainsKey(mdef))
					throw new SymbolicAnalysisFailedException();
				finallyMethodToStateRange.Add(mdef, range);
			}
			rangeAnalysis = null;
		}
		#endregion
		
		#region Analysis of MoveNext()
		ILVariable returnVariable;
		ILLabel returnLabel;
		ILLabel returnFalseLabel;
		ILLabel returnTrueLabel;

		void AnalyzeMoveNext()
		{
			//MethodDefinition 
			ILBlock ilMethod = CreateILAst(moveNextMethod);
			
			if (ilMethod.Body.Count == 0)
				throw new SymbolicAnalysisFailedException();
			ILExpression lastReturnArg;
			if (!ilMethod.Body.Last().Match(ILCode.Ret, out lastReturnArg))
				throw new SymbolicAnalysisFailedException();

			int returnFalseLabelPos = -1;
			int returnTrueLabelPos = -3;
			// There are two possibilities:
			if (lastReturnArg.Code == ILCode.Ldloc) {
				// a) the compiler uses a variable for returns (in debug builds, or when there are try-finally blocks)
				returnVariable = (ILVariable)lastReturnArg.Operand;
				returnLabel = ilMethod.Body.ElementAtOrDefault(ilMethod.Body.Count - 2) as ILLabel;
				if (returnLabel == null)
					throw new SymbolicAnalysisFailedException();
			} else {
				// b) the compiler directly returns constants
				returnVariable = null;
				returnLabel = null;
				// In this case, the last return must return false.
				if (lastReturnArg.Code != ILCode.Ldc_I4)
				    
				{
					throw new SymbolicAnalysisFailedException ();
				}

				if( (int)lastReturnArg.Operand != 0) 
				{
					returnFalseLabelPos = -3;
					returnTrueLabelPos = -1;
				}

			}
	

			
			ILTryCatchBlock tryFaultBlock = ilMethod.Body[0] as ILTryCatchBlock;
			List<ILNode> body;
			int bodyLength;
			if (tryFaultBlock != null) {
				// there are try-finally blocks
				if (returnVariable == null) // in this case, we must use a return variable
					throw new SymbolicAnalysisFailedException();
				// must be a try-fault block:
				if (tryFaultBlock.CatchBlocks.Count != 0 || tryFaultBlock.FinallyBlock != null || tryFaultBlock.FaultBlock == null)
					throw new SymbolicAnalysisFailedException();
				
				ILBlock faultBlock = tryFaultBlock.FaultBlock;
				// Ensure the fault block contains the call to Dispose().
				if (faultBlock.Body.Count != 2)
					throw new SymbolicAnalysisFailedException();
				MethodReference disposeMethodRef;
				ILExpression disposeArg;
				if (!faultBlock.Body[0].Match(ILCode.Call, out disposeMethodRef, out disposeArg))
					throw new SymbolicAnalysisFailedException();
				if (GetMethodDefinition(disposeMethodRef) != disposeMethod || !disposeArg.MatchThis())
					throw new SymbolicAnalysisFailedException();
				if (!faultBlock.Body[1].Match(ILCode.Endfinally))
					throw new SymbolicAnalysisFailedException();
				
				body = tryFaultBlock.TryBlock.Body;
				bodyLength = body.Count;
			} else {
				// no try-finally blocks
				body = ilMethod.Body;
				if (returnVariable == null)
					bodyLength = body.Count - 1; // all except for the return statement
				else
					bodyLength = body.Count - 2; // all except for the return label and statement
			}
			
			// Now verify that the last instruction in the body is 'ret(false)'
			if (returnVariable != null) {
				// If we don't have a return variable, we already verified that above.
				// If we do have one, check for 'stloc(returnVariable, ldc.i4(0))'
				
				// Maybe might be a jump to the return label after the stloc:
				ILExpression leave = body.ElementAtOrDefault(bodyLength - 1) as ILExpression;
				if (leave != null && (leave.Code == ILCode.Br || leave.Code == ILCode.Leave) && leave.Operand == returnLabel)
					bodyLength--;
				ILExpression store0 = body.ElementAtOrDefault(bodyLength - 1) as ILExpression;
				if (store0 == null || store0.Code != ILCode.Stloc || store0.Operand != returnVariable)
					throw new SymbolicAnalysisFailedException();
				if (store0.Arguments[0].Code != ILCode.Ldc_I4 || (int)store0.Arguments[0].Operand != 0)
					throw new SymbolicAnalysisFailedException();
				
				bodyLength--; // don't conside the stloc instruction to be part of the body
			}
			// The last element in the body usually is a label pointing to the 'ret(false)'
			returnFalseLabel =  ilMethod.Body.ElementAtOrDefault(bodyLength +returnFalseLabelPos) as ILLabel;
			returnTrueLabel  =  ilMethod.Body.ElementAtOrDefault(bodyLength +returnTrueLabelPos) as ILLabel;

			// Note: in Roslyn-compiled code, returnFalseLabel may be null.
			
			var rangeAnalysis = new StateRangeAnalysis(body[0], StateRangeAnalysisMode.IteratorMoveNext, stateField);
			int pos = rangeAnalysis.AssignStateRanges(body, bodyLength);
			rangeAnalysis.EnsureLabelAtPos(body, ref pos, ref bodyLength);
			
			var labels = rangeAnalysis.CreateLabelRangeMapping(body, pos, bodyLength);
			ConvertBody(body, pos, bodyLength, labels);
		}
		#endregion



		bool isFlowControlCode(ILCode c)
		{
			switch (c) {
			case ILCode.__Brfalse_S:
			case ILCode.__Brtrue_S:
			case ILCode.__Beq_S:
			case ILCode.__Bge_S:
			case ILCode.__Bgt_S:
			case ILCode.__Ble_S:
			case ILCode.__Blt_S:
			case ILCode.__Bne_Un_S:
			case ILCode.__Bge_Un_S:
			case ILCode.__Bgt_Un_S:
			case ILCode.__Ble_Un_S:
			case ILCode.__Blt_Un_S:
			case ILCode.__Brfalse:
			case ILCode.Brtrue:
			case ILCode.__Beq:
			case ILCode.__Bge:
			case ILCode.__Bgt:
			case ILCode.__Ble:
			case ILCode.__Blt:
			case ILCode.__Bne_Un:
			case ILCode.__Bge_Un:
			case ILCode.__Bgt_Un:
			case ILCode.__Ble_Un:
			case ILCode.__Blt_Un:
				return true;
			default:
				break;
			}

			return false;
		}

		bool isMatchControlBr(ILCode c, int value)
		{
			switch (c) {
			case ILCode.__Beq_S:
			case ILCode.__Bge_S:
			case ILCode.__Bgt_S:
			case ILCode.__Ble_S:
			case ILCode.__Blt_S:
			case ILCode.__Bne_Un_S:
			case ILCode.__Bge_Un_S:
			case ILCode.__Bgt_Un_S:
			case ILCode.__Ble_Un_S:
			case ILCode.__Blt_Un_S:
			case ILCode.__Beq:
			case ILCode.__Bge:
			case ILCode.__Bgt:
			case ILCode.__Ble:
			case ILCode.__Blt:
			case ILCode.__Bne_Un:
			case ILCode.__Bge_Un:
			case ILCode.__Bgt_Un:
			case ILCode.__Ble_Un:
			case ILCode.__Blt_Un:
				break;

			case ILCode.__Brfalse_S:
			case ILCode.__Brfalse:
				return value == 0;
			case ILCode.__Brtrue_S:
			case ILCode.Brtrue:
				return value != 0;
			default:
				break;
			}

			return false;	
		}
		void ScanLine(YieldVMStat vm, int start, int stop)
		{

			int m = start;



			while (m < stop)
			{
				ILNode node = vm.usefulList [m];
				bool skipNode = false;
				bool jumpped  = false;
				if (node is ILExpression) {
					ILExpression expr = node as ILExpression;
					if (expr.Code == ILCode.Switch) {

						int pendingSwitchExprJumpLableIndex = -1;
						bool isStateSwithPending = false;
						int finalStatVal = 0;

						if (expr.Arguments [0].Code == ILCode.Ldloc && expr.Arguments [0].Operand == vm.statILVariable) {
							isStateSwithPending = true;
							pendingSwitchExprJumpLableIndex = vm.localStat;
							finalStatVal = vm.localStat;
						} else if (expr.Arguments [0].Code == ILCode.Ldfld &&
						           GetFieldDefinition (expr.Arguments [0].Operand as FieldReference) == this.stateField) {
							isStateSwithPending = true;
							pendingSwitchExprJumpLableIndex = vm.fieldStat;
							finalStatVal = vm.fieldStat;
						} else if (expr.Arguments [0].Code == ILCode.Sub) {
							ILExpression iLExpression10 = expr.Arguments [0];
							if (iLExpression10.Arguments [0].Code == ILCode.Ldloc && iLExpression10.Arguments [0].Operand == vm.statILVariable) {
								if (iLExpression10.Arguments [1].Code == ILCode.Ldc_I4) {
									finalStatVal = vm.localStat;
									pendingSwitchExprJumpLableIndex = vm.localStat - (int)iLExpression10.Arguments [1].Operand;
									isStateSwithPending = true;
								}
							} else if (iLExpression10.Arguments [0].Code == ILCode.Ldfld &&
							           GetFieldDefinition (iLExpression10.Arguments [0].Operand as FieldReference) == this.stateField) {
								if (iLExpression10.Arguments [1].Code == ILCode.Ldc_I4) {
									finalStatVal = vm.fieldStat;
									pendingSwitchExprJumpLableIndex = vm.fieldStat - (int)iLExpression10.Arguments [1].Operand;
									isStateSwithPending = true;
								}
							}
						}

						


						if (isStateSwithPending) {
							

							ILLabel jmpLabel = null;
							ILLabel[] lbList = expr.Operand as ILLabel[];
							if (pendingSwitchExprJumpLableIndex >= 0 &&
							    pendingSwitchExprJumpLableIndex < lbList.Length) {
								jmpLabel = lbList [pendingSwitchExprJumpLableIndex];
							} else {
								if (m + 1 >= vm.usefulList.Count
								    || !(vm.usefulList [m + 1] is ILExpression)
								    || (vm.usefulList [m + 1] as ILExpression).Code != ILCode.Br) {
									//impossible 
									throw new SymbolicAnalysisFailedException (); 
								}
								ILExpression switchDefault = vm.usefulList [m + 1] as ILExpression;
								jmpLabel = switchDefault.Operand as ILLabel;
							}

							//convert switch to jmp


							if (pendingSwitchExprJumpLableIndex >= 0 &&
							    pendingSwitchExprJumpLableIndex < lbList.Length) {
								bool addedJump = false;
								if (expr == vm.firstStatSwitchExpr) {
									addedJump = vm.addNewNode (new ILExpression (ILCode.Br, jmpLabel), m);
									if (!addedJump && vm.fieldStatChangedPos.ContainsKey (finalStatVal)) {
										int stateStartPos = vm.fieldStatChangedPos [finalStatVal];
										addedJump = vm.addNewNode (new ILExpression (ILCode.Br, jmpLabel), stateStartPos);
									}

									vm.localStatChangedPos.Clear ();
								} else {
									
									if (vm.localStatChangedPos.ContainsKey (finalStatVal)) {
										int stateStartPos = vm.localStatChangedPos [finalStatVal];

										addedJump = vm.addNewNode (new ILExpression (ILCode.Br, jmpLabel), stateStartPos);

                                        if (!addedJump)
                                        {
                                            if (!vm.addNewNode(new ILExpression(ILCode.Br, jmpLabel), m))
                                            {
                                                //throw new SymbolicAnalysisFailedException (); 
                                            }
                                        }
									}
                                  
								}
								
							} else {
								vm.addNewNode (new ILExpression (ILCode.Br, jmpLabel), m );
							}

							vm.localStatChangedPos.Clear ();
							vm.markingLocalChangePos = false;


							m = vm.labelMaping [jmpLabel];
							skipNode = true;
							jumpped = true;
						} else {
							
							if (vm.markingLocalChangePos) {
								vm.localStatChangedPos.Clear ();
								vm.markingLocalChangePos = false;
							}

							List<int> jumplist = new List<int> ();
							ILLabel[] lbList = expr.Operand as ILLabel[];
							foreach (ILLabel tmp in lbList) {
								jumplist.Add (vm.labelMaping [tmp]);
							}
							jumplist.Add (m + 1); // the default pos;

                            if (!vm.addNewNode(node, m))
                                return;
							skipNode = true;
							foreach (int tmp in jumplist) {
								ScanLine (vm, tmp, vm.usefulList.Count);
							}
							return;
						}
							
					} else if (expr.Code == ILCode.Br || expr.Code == ILCode.Leave) {
						ILLabel lb = expr.Operand as ILLabel;
						if (lb == null) {
							//impossible 
							throw new SymbolicAnalysisFailedException (); 
						}

						int lbPos = vm.labelMaping [lb];
                        if (!vm.addNewNode(node, m))
                            return;
						if (lbPos < m)
							ScanLine (vm, lbPos, m);
						else
                            ScanLine(vm, lbPos, vm.usefulList.Count);
					
						return;

					} else if (isFlowControlCode (expr.Code)) {
						ILLabel lb = expr.Operand as ILLabel;
						if (lb == null) {
							//impossible 
							throw new SymbolicAnalysisFailedException (); 
						}


						if (expr == vm.firstStatSwitchExpr) {

							int testvalue = 0;
							skipNode = true;

							if ((expr.Arguments [0].Code == ILCode.Ldfld
							    && GetFieldDefinition (expr.Arguments [0].Operand as FieldReference) == stateField)) {
								testvalue = vm.fieldStat;
							} else
								testvalue = vm.localStat;

							if (isMatchControlBr (expr.Code, testvalue)) {
								vm.addNewNode (new ILExpression (ILCode.Br, lb), m);
								m = vm.labelMaping [lb];
								jumpped = true;
							} else {
								vm.addNewNode (new ILExpression (ILCode.Nop, null, new List<ILExpression>()), m);
								Console.WriteLine ("not maching, should skip this and continue");
							}

							if (vm.markingLocalChangePos) {
								vm.localStatChangedPos.Clear ();
								vm.markingLocalChangePos = false;
							}
						} else {
							if (vm.markingLocalChangePos) {
								vm.localStatChangedPos.Clear ();
								vm.markingLocalChangePos = false;
							}

							int lbPos = vm.labelMaping [lb];
							if (!vm.addNewNode (node, m))
								return;
							if (lbPos < m) {
								ScanLine (vm, lbPos, m);
								ScanLine (vm, m + 1, vm.usefulList.Count);

							} else {
								ScanLine (vm, m + 1, lbPos);
								ScanLine (vm, lbPos, vm.usefulList.Count);
							}
							return;
						}


					} else if (expr.Code == ILCode.Stloc) {
						if (expr.Operand == vm.statILVariable) {
							
							if (expr.Arguments [0].Code == ILCode.Ldc_I4) {
								vm.localStat = (int)expr.Arguments [0].Operand;
								vm.localStatChangedPos [vm.localStat] = m;
							} else if (expr.Arguments [0].Code == ILCode.Ldfld
							           && GetFieldDefinition (expr.Arguments [0].Operand as FieldReference) == stateField) {


								vm.localStat = vm.fieldStat;
								//mark the local state for use when we process jump
								vm.localStatChangedPos.Clear ();
								vm.markingLocalChangePos = true;
								vm.localStatChangedPos.Add (vm.localStat, m);

							} else {
								throw new SymbolicAnalysisFailedException ();
							}
							skipNode = true;
						} else if (expr.Operand == vm.diposeFlagILVariable) {
							if (expr.Arguments [0].Code != ILCode.Ldc_I4) {
								throw new SymbolicAnalysisFailedException ();
							}
							vm.localDisposeFlag = (int)expr.Arguments [0].Operand;
							skipNode = true;
						} else if (expr.Operand == returnVariable) {
							if (expr.Arguments [0].Code != ILCode.Ldc_I4) {
								throw new SymbolicAnalysisFailedException ();
							}
							vm.localReturnValue = (int)expr.Arguments [0].Operand;
							skipNode = true;
						}

					} else if (expr.Code == ILCode.Stfld) {
						if (expr.Arguments [0].MatchThis ()) {
							if (YieldReturnDecompiler.GetFieldDefinition (expr.Operand as FieldReference) == this.stateField) {
								if (expr.Arguments [1].Code != ILCode.Ldc_I4) {
									throw new SymbolicAnalysisFailedException ();
								}
								vm.fieldStat = (int)expr.Arguments [1].Operand;

								skipNode = true;
							} else if (YieldReturnDecompiler.GetFieldDefinition (expr.Operand as FieldReference) == this.currentField) {
								//state should change
								vm.addNewNode (new ILExpression (ILCode.YieldReturn, null, expr.Arguments [1]), m);

								if (m + 1 < vm.usefulList.Count) {
									ILExpression nextExpr = vm.usefulList [m + 1] as ILExpression;

									if (nextExpr.Code != ILCode.Stfld && (m + 2< vm.usefulList.Count)
									    &&  (vm.usefulList [m + 2] as ILExpression).Code ==ILCode.Stfld )
									{
										nextExpr = vm.usefulList[m + 2] as ILExpression;
									}

									if (
										nextExpr.Code == ILCode.Stfld
										&& YieldReturnDecompiler.GetFieldDefinition (nextExpr.Operand as FieldReference) == this.stateField) {
										vm.fieldStat = (int)nextExpr.Arguments [1].Operand;
										vm.fieldStatChangedPos [vm.fieldStat] = m + 1;

									}
										
									else
									{

										throw new SymbolicAnalysisFailedException ();
									}
								} else {
									throw new SymbolicAnalysisFailedException ();
								}

								skipNode = true;
								return;
							}
						}
					} else if (expr.Code == ILCode.Ret) {
						if (expr.Arguments [0].Code == ILCode.Ldloc &&
						    expr.Arguments [0].Operand == returnVariable) {
							if (vm.localReturnValue == 0) {
								skipNode = true;
								vm.addNewNode (new ILExpression (ILCode.YieldBreak, null), m);
							} else {
								//todo nothing.
								skipNode = true;
								vm.addNewNode (new ILExpression (ILCode.Ret, null, new List<ILExpression> ()), m);
							}
						} else if (expr.Arguments [0].Code == ILCode.Ldc_I4) {
							int retval = (int)expr.Arguments [0].Operand;
							if (retval == 0) {
								skipNode = true;
								vm.addNewNode (new ILExpression (ILCode.YieldBreak, null), m);
							} else {
								//todo nothing.
								skipNode = true;
								vm.addNewNode (new ILExpression (ILCode.Ret, null, new List<ILExpression> ()), m);
							}
						} else {
							throw new SymbolicAnalysisFailedException ();
						}

					}

				} 


				if (!skipNode) {
					if(!vm.addNewNode(node,m))
                    {
                        return;
                    }
				}
				if (!jumpped) {
					m++;
				}
				
			}

		}

		bool AnalyzeMoveNext2()
		{
			ILBlock ilMethod = CreateILAst(moveNextMethod);
			ILExpression lastReturnArg;
			if (!ilMethod.Body.Last().Match(ILCode.Ret, out lastReturnArg))
				throw new SymbolicAnalysisFailedException();

//			if (ilMethod.Body.Count == 6 || ilMethod.Body.Count == 5) {
//			
//				Console.WriteLine ("Debug Pos" + ilMethod.ToString ());
//			}

//			if (ilMethod.Body.Count == 24) {
//
//				Console.WriteLine ("Debug Pos2 " + ilMethod.ToString ());
//			}
			 
//			ILVariable stateVar=null;
//			ILVariable diposeFlagVar=null;
			ILLabel lastLabel = null;
		
			//can once to retrive pc , current , pc's local, usage ILNode
			YieldVMStat vm = new YieldVMStat();
		
			for (int i = 0; i < ilMethod.Body.Count; i++) {
				ILNode node = ilMethod.Body [i];

				if (node is ILExpression) {
					ILExpression expr = node as ILExpression;
					switch (expr.Code) {
					case ILCode.Stloc:
						//if arments is stateField;
						//set statelocal = local;
						if (vm.statILVariable == null && expr.Arguments [0].Code == ILCode.Ldfld
						    && GetFieldDefinition (expr.Arguments [0].Operand as FieldReference) == stateField) {
							vm.statILVariable = expr.Operand as ILVariable;
						} 
						break;
					case ILCode.Ret:
						//find out the return Jump Lable and stat
						ILExpression retArg = expr.Arguments [0] as ILExpression;

						if (retArg.Code == ILCode.Ldloc) {
							// a) the compiler uses a variable for returns (in debug builds, or when there are try-finally blocks)
							returnVariable = (ILVariable)lastReturnArg.Operand;
							returnLabel = ilMethod.Body.ElementAtOrDefault (ilMethod.Body.Count - 2) as ILLabel;
							if (returnLabel == null)
								throw new SymbolicAnalysisFailedException ();
						} else {
							// b) the compiler directly returns constants
							if (retArg.Code != ILCode.Ldc_I4) {
								throw new SymbolicAnalysisFailedException ();
							}

							if ((int)retArg.Operand == 0) {
								returnFalseLabel = lastLabel;
							} else {
								returnTrueLabel = lastLabel;
							}

						}

						break;
					}
					vm.usefulList.Add (expr);
				} 
				else if (node is ILTryCatchBlock) {
					ILTryCatchBlock tryBlock = node as ILTryCatchBlock;
					//check if a state try
					//finally block is not null, contains dispose , fault block is null, catch block.count==0
					//exand it to usageList
					bool isStateTry = false;
					if (tryBlock.FaultBlock == null && tryBlock.CatchBlocks.Count == 0 && tryBlock.FinallyBlock != null) {
						foreach (var tn in tryBlock.FinallyBlock.Body) {
							
							if (tn is ILExpression && (tn as ILExpression).Code ==ILCode.Callvirt ) 
							{
								ILExpression tnexp = (tn as ILExpression);
								if ( (tnexp.Operand as MethodReference).Name=="Dispose"
									&& tnexp.Arguments.Count==1
									&& tnexp.Arguments [0] is ILExpression
								) {
									ILExpression disposeArg = tnexp.Arguments [0];
									if (disposeArg.Arguments.Count==1 && disposeArg.Arguments [0].MatchThis ()) {
										isStateTry = true;
									}
								}
							}
						}
					}
					if (isStateTry) {
						foreach (ILNode blkNode in tryBlock.TryBlock.Body) {
							vm.usefulList.Add (blkNode);
						}

						foreach (ILNode blkNode in tryBlock.FinallyBlock.Body) {
							if (blkNode is ILExpression) {
								ILExpression blkExpr = blkNode as ILExpression;
								if (blkExpr.Code == ILCode.Stloc && vm.diposeFlagILVariable == null && blkExpr.Operand != vm.statILVariable && blkExpr.Arguments [0].Code == ILCode.Ldc_I4) {
									vm.diposeFlagILVariable = blkExpr.Operand as ILVariable;
								}
							}
						}
					} else {
						
						if (tryBlock.TryBlock != null) {
							foreach (ILNode blkNode in tryBlock.TryBlock.Body) {
								vm.usefulList.Add (blkNode);
								vm.tryBlockMapping [vm.usefulList.Count - 1] = new ILTryCatchBlockInfo(){blk=tryBlock,nodelistRef=tryBlock.TryBlock.Body};
							}
							tryBlock.TryBlock.Body.Clear ();
						}


						if (tryBlock.CatchBlocks.Count != 0) {
							foreach (ILTryCatchBlock.CatchBlock blk in tryBlock.CatchBlocks) {
								foreach (ILNode blkNode in blk.Body) {
									vm.usefulList.Add (blkNode);
									vm.tryBlockMapping [vm.usefulList.Count - 1] = new ILTryCatchBlockInfo () {
										blk = tryBlock,
										nodelistRef = blk.Body
									};
								}
								blk.Body.Clear ();
							}
						}

						if (tryBlock.FinallyBlock != null) {
							foreach (ILNode blkNode in tryBlock.FinallyBlock.Body) {
								vm.usefulList.Add (blkNode);
								vm.tryBlockMapping [vm.usefulList.Count - 1] = new ILTryCatchBlockInfo(){blk=tryBlock,nodelistRef=tryBlock.FinallyBlock.Body};
							}
							tryBlock.FinallyBlock.Body.Clear ();
						}



						if (tryBlock.FaultBlock != null) {
							foreach (ILNode blkNode in tryBlock.FaultBlock.Body) {
								vm.usefulList.Add (blkNode);
								vm.tryBlockMapping [vm.usefulList.Count - 1] = new ILTryCatchBlockInfo(){blk=tryBlock,nodelistRef=tryBlock.FaultBlock.Body};
							}
							tryBlock.FaultBlock.Body.Clear ();
						}



 						

					}
				} else if (node is ILLabel) {
					lastLabel = node as ILLabel;
					vm.usefulList.Add (node);
				}
				else {
					//add it to usageList
					vm.usefulList.Add (node);
				}
			}

	


			//make all the lable and get first case swith 
			//
			
			bool expectedSwitchFollowBr = false;
			for (int i = 0; i < vm.usefulList.Count; i++) {
				ILNode node = vm.usefulList [i];
				if (node is ILExpression) {
					ILExpression expr = node as ILExpression;
					if (expr.Code == ILCode.Switch
					    && vm.firstStatSwitchExpr == null
					    && expr.Arguments.Count == 1) {
						if ((expr.Arguments [0].Code == ILCode.Ldloc
						    && expr.Arguments [0].Operand == vm.statILVariable)

						    ||

						    (expr.Arguments [0].Code == ILCode.Ldfld
						    && GetFieldDefinition (expr.Arguments [0].Operand as FieldReference) == stateField)) {
						
							foreach (ILLabel lb in (expr.Operand as ILLabel[])) {
								vm.fistStateSwitchLabelList.Add (lb);
							}
							vm.firstStatSwitchExpr = expr;
							expectedSwitchFollowBr = true;
						}
					} else if (isFlowControlCode (expr.Code)
					           && expr.Arguments.Count>0 && expr.Arguments[0].Operand==vm.statILVariable
					          ) 
					{
						ILLabel lb = expr.Operand as ILLabel;
						vm.fistStateSwitchLabelList.Add (lb);
						vm.firstStatSwitchExpr = expr;
					}
					else if (expr.Code == ILCode.Br && expectedSwitchFollowBr) {
						expectedSwitchFollowBr = false;
						if (initPCValue < 0) {
							vm.fistStateSwitchLabelList.Insert (0, expr.Operand as ILLabel);
						} else {
							vm.fistStateSwitchLabelList.Add (expr.Operand as ILLabel);

						}
					}

				} else if (node is ILTryCatchBlock) {
					
				}
				else if (node is ILLabel) {
					vm.labelMaping [(node as ILLabel)] = i;
				}
			}


			int scanTimes = vm.fistStateSwitchLabelList.Count;

//			if (initPCValue == 0)
//				scanTimes -= 1;
			//scan all first swith stat 
			//
			for(int i=0; i<scanTimes;i++)
			{
				vm.resetStatVariable ();
				if(initPCValue==0  )
				{
					vm.fieldStat=i;
				}
				else 
				{
					if(i==0)
					{
						vm.fieldStat=initPCValue;
					}
					else 
					{
						vm.fieldStat =i-1;
					}
				}
				vm.scanningStat = vm.fieldStat;
				ScanLine (vm,0,vm.usefulList.Count);
                if(vm.fieldStatChangedPos.Count==0 && i==0 && i<scanTimes && initPCValue<0)
                {
                    Console.WriteLine("A compiler bug? .fieldStatChangedPos.Count==0 && i==0 && i<scanTimes && initPCValue<0 at method: " + moveNextMethod.FullName + "\n:" + vm.outputNewBody());
                    vm.clearCreatedNodes();
					return false;
                }
			}

//			vm.fieldStat=initPCValue;
//			ScanLine (vm, 0);

			createdBody = vm.outputNewBody ();
//			if (ilMethod.Body.Count == 6 || ilMethod.Body.Count == 5) {
//				Console.WriteLine ("xxxx size " + createdBody.Count);
//			}
//			if (createdBody.Count == 24) {
//				Console.WriteLine ("debug pos " + createdBody.ToString ());
//			}
			Console.WriteLine ("create size " + createdBody.Count);
			return true;

		}
		#region ConvertBody
		struct SetState
		{
			public readonly int NewBodyPos;
			public readonly int NewState;
			
			public SetState(int newBodyPos, int newState)
			{
				this.NewBodyPos = newBodyPos;
				this.NewState = newState;
			}
		}
		
		void ConvertBody(List<ILNode> body, int startPos, int bodyLength, List<KeyValuePair<ILLabel, StateRange>> labels)
		{
			createdBody = new List<ILNode>();
		//	newBody.Add(MakeGoTo(labels, 0));
			List<SetState> stateChanges = new List<SetState>();
			int currentState = -1;
			// Copy all instructions from the old body to newBody.
			for (int pos = startPos; pos < bodyLength; pos++) {
				ILExpression expr = body[pos] as ILExpression;
				if (expr != null && expr.Code == ILCode.Stfld && expr.Arguments [0].MatchThis ()) {
					// Handle stores to 'state' or 'current'
					if (GetFieldDefinition (expr.Operand as FieldReference) == stateField) {
						if (expr.Arguments [1].Code != ILCode.Ldc_I4)
							throw new SymbolicAnalysisFailedException ();
						currentState = (int)expr.Arguments [1].Operand;
						stateChanges.Add (new SetState (createdBody.Count, currentState));
						ILExpression nextExpr = (pos + 1 < bodyLength) ? (body [pos + 1] as ILExpression) : null;
						if (nextExpr != null && nextExpr.Operand == returnTrueLabel) {
							pos++;
							continue;
						}

					} else if (GetFieldDefinition (expr.Operand as FieldReference) == currentField) {
						createdBody.Add (new ILExpression (ILCode.YieldReturn, null, expr.Arguments [1]));

					} else {
						createdBody.Add (body [pos]);
					}
				} else if (returnVariable != null && expr != null && expr.Code == ILCode.Stloc && expr.Operand == returnVariable) {
					// handle store+branch to the returnVariable
					ILExpression br = body.ElementAtOrDefault (++pos) as ILExpression;
					if (br == null || !(br.Code == ILCode.Br || br.Code == ILCode.Leave) || br.Operand != returnLabel || expr.Arguments [0].Code != ILCode.Ldc_I4)
						throw new SymbolicAnalysisFailedException ();
					int val = (int)expr.Arguments [0].Operand;
					if (val == 0) {
						createdBody.Add (new ILExpression (ILCode.YieldBreak, null));
					} else if (val == 1) {
						createdBody.Add (MakeGoTo (labels, currentState));
					} else {
						throw new SymbolicAnalysisFailedException ();
					}
				} else if (expr != null && expr.Code == ILCode.Ret) {
					if (expr.Arguments.Count != 1 || expr.Arguments [0].Code != ILCode.Ldc_I4)
						throw new SymbolicAnalysisFailedException ();
					// handle direct return (e.g. in release builds)
					int val = (int)expr.Arguments [0].Operand;
					if (val == 0) {
						createdBody.Add (new ILExpression (ILCode.YieldBreak, null));
					} else if (val == 1) {
						createdBody.Add (MakeGoTo (labels, currentState));
					} else {
						throw new SymbolicAnalysisFailedException ();
					}
				} else if (expr != null && expr.Code == ILCode.Call && expr.Arguments.Count == 1 && expr.Arguments [0].MatchThis ()) {
					MethodDefinition method = GetMethodDefinition (expr.Operand as MethodReference);
					if (method == null)
						throw new SymbolicAnalysisFailedException ();
					StateRange stateRange;
					if (method == disposeMethod) {
						// Explicit call to dispose is used for "yield break;" within the method.
						ILExpression br = body.ElementAtOrDefault (++pos) as ILExpression;
						if (br == null || !(br.Code == ILCode.Br || br.Code == ILCode.Leave) || br.Operand != returnFalseLabel)
							throw new SymbolicAnalysisFailedException ();
						createdBody.Add (new ILExpression (ILCode.YieldBreak, null));
					} else if (finallyMethodToStateRange.TryGetValue (method, out stateRange)) {
						// Call to Finally-method
						int index = stateChanges.FindIndex (ss => stateRange.Contains (ss.NewState));
						if (index < 0)
							throw new SymbolicAnalysisFailedException ();
						
						ILLabel label = new ILLabel ();
						label.Name = "JumpOutOfTryFinally" + stateChanges [index].NewState;
						createdBody.Add (new ILExpression (ILCode.Leave, label));
						
						SetState stateChange = stateChanges [index];
						// Move all instructions from stateChange.Pos to newBody.Count into a try-block
						stateChanges.RemoveRange (index, stateChanges.Count - index); // remove all state changes up to the one we found
						ILTryCatchBlock tryFinally = new ILTryCatchBlock ();
						tryFinally.TryBlock = new ILBlock (createdBody.GetRange (stateChange.NewBodyPos, createdBody.Count - stateChange.NewBodyPos));
						createdBody.RemoveRange (stateChange.NewBodyPos, createdBody.Count - stateChange.NewBodyPos); // remove all nodes that we just moved into the try block
						tryFinally.CatchBlocks = new List<ILTryCatchBlock.CatchBlock> ();
						tryFinally.FinallyBlock = ConvertFinallyBlock (method);
						createdBody.Add (tryFinally);
						createdBody.Add (label);
					}
				}
				else {
					createdBody.Add(body[pos]);
				}
			}
	
		}
		
		ILExpression MakeGoTo(ILLabel targetLabel)
		{
			Debug.Assert(targetLabel != null);
			if (targetLabel == returnFalseLabel)
				return new ILExpression(ILCode.YieldBreak, null);
			else
				return new ILExpression(ILCode.Br, targetLabel);
		}
		
		ILExpression MakeGoTo(List<KeyValuePair<ILLabel, StateRange>> labels, int state)
		{
			foreach (var pair in labels) {
				if (pair.Value.Contains(state))
					return MakeGoTo(pair.Key);
			}
			throw new SymbolicAnalysisFailedException();
		}
		
		ILBlock ConvertFinallyBlock(MethodDefinition finallyMethod)
		{
			ILBlock block = CreateILAst(finallyMethod);
			// Get rid of assignment to state
			FieldReference stfld;
			List<ILExpression> args;
			if (block.Body.Count > 0 && block.Body[0].Match(ILCode.Stfld, out stfld, out args)) {
				if (GetFieldDefinition(stfld) == stateField && args[0].MatchThis())
					block.Body.RemoveAt(0);
			}
			// Convert ret to endfinally
			foreach (ILExpression expr in block.GetSelfAndChildrenRecursive<ILExpression>()) {
				if (expr.Code == ILCode.Ret)
					expr.Code = ILCode.Endfinally;
			}
			return block;
		}
		#endregion
		
		#region TranslateFieldsToLocalAccess
		void TranslateFieldsToLocalAccess()
		{
			TranslateFieldsToLocalAccess(createdBody, fieldToParameterMap);
		}
		
		internal static void TranslateFieldsToLocalAccess(List<ILNode> newBody, Dictionary<FieldDefinition, ILVariable> fieldToParameterMap)
		{
			var fieldToLocalMap = new DefaultDictionary<FieldDefinition, ILVariable>(f => new ILVariable { Name = f.Name, Type = f.FieldType });
			foreach (ILNode node in newBody) {
				foreach (ILExpression expr in node.GetSelfAndChildrenRecursive<ILExpression>()) {
					FieldDefinition field = GetFieldDefinition(expr.Operand as FieldReference);
					if (field != null) {
						switch (expr.Code) {
							case ILCode.Ldfld:
								if (expr.Arguments[0].MatchThis() ) {
									expr.Code = ILCode.Ldloc;
									if (fieldToParameterMap.ContainsKey(field)) {
										expr.Operand = fieldToParameterMap[field];
									} else {
										expr.Operand = fieldToLocalMap[field];
									}
									expr.Arguments.Clear();
								}
								break;
							case ILCode.Stfld:
								if (expr.Arguments[0].MatchThis()) {
									expr.Code = ILCode.Stloc;
									if (fieldToParameterMap.ContainsKey(field)) {
										expr.Operand = fieldToParameterMap[field];
									} else {
										expr.Operand = fieldToLocalMap[field];
									
									}
									expr.Arguments.RemoveAt(0);
								}
								break;
							case ILCode.Ldflda:
								if (expr.Arguments[0].MatchThis()) {
									expr.Code = ILCode.Ldloca;
									if (fieldToParameterMap.ContainsKey(field)) {
										expr.Operand = fieldToParameterMap[field];
									} else {
										expr.Operand = fieldToLocalMap[field];
									}


									expr.Arguments.Clear();
								}
								break;
						}
					}
				}
			}
		}
		#endregion
	}
}
 